package codeGeneration;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import util.ClassInformation;
import util.Configuration;
import util.FolderPath;
import util.Tag;

public class ConfigGenerator extends Generator {
	private Configuration config;
	private FolderPath folderPath;

	public ConfigGenerator(Configuration config, FolderPath folderPath) {
		super();
		this.config = config;
		this.folderPath = folderPath;

		loadDefaultServletContext();
	}

	public void generate(String fileName) throws IOException {
		FileWriter fileWriter = null;

		if ("web.xml".equalsIgnoreCase(fileName))
			generateWebConfig((fileWriter = getFileWriter(folderPath
					.getWebinfPath() + '/' + fileName)));
		if ("root-context.xml".equalsIgnoreCase(fileName))
			generateRootContextConfig((fileWriter = getFileWriter(folderPath
					.getWebinfPath() + "/spring/" + fileName)));
		if ("servlet-context.xml".equalsIgnoreCase(fileName))
			generateServletContextConfig((fileWriter = getFileWriter(folderPath
					.getWebinfPath()
					+ "/spring"
					+ "/appServlet"
					+ '/'
					+ fileName)));

		closeFileWriter(fileWriter);
	}

	private void loadDefaultServletContext() {
		loadDefaultMessageFile();

		Properties prop = new Properties();
		prop.put("prefix", "/WEB-INF/views/");
		prop.put("suffix", ".jsp");

		ClassInformation classInformation = config
				.checkWithClassOfBean("org.springframework.web.servlet.view."
						+ "InternalResourceViewResolver");
		classInformation.setId(null);
		classInformation.setChildTags(Tag.makeTagForBeanProperty(prop));
	}

	private void loadDefaultMessageFile() {
		loadMessageFile("/WEB-INF/messages", "messages");
	}

	private void loadMessageFile(String path, String fileName) {
		Properties prop = new Properties();
		prop.put("basename", path + '/' + fileName);

		ClassInformation classInformation = config
				.checkWithIDOfBean("messageSource");
		classInformation.setClassName("org.springframework.context.support."
				+ "ReloadableResourceBundleMessageSource");
		classInformation.setChildTags(Tag.makeTagForBeanProperty(prop));
	}

	private void generateRootContextConfig(FileWriter fileWriter)
			throws IOException {
		Properties prop = new Properties();
		prop.put("version", "1.0");
		prop.put("encoding", "UTF-8");
		indentOneTime("<?xml", prop, "?>", fileWriter, true);
		prop.clear();

		prop.put("xmlns", "http://www.springframework.org/schema/beans");
		prop.put("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
		prop.put(
				"xsi:schemaLocation",
				"http://www.springframework.org/schema/beans "
						+ "http://www.springframework.org/schema/beans/spring-beans.xsd");
		indentOneTime("<beans", prop, ">", fileWriter, false);

		appendToFile(fileWriter, "<!-- Root Context: defines shared resources "
				+ "visible to all other web components -->\n");

		decreaseIndentOneTime("</beans>", fileWriter);
	}

	private void generateServletContextConfig(FileWriter fileWriter)
			throws IOException {
		Properties prop = new Properties();
		prop.put("version", "1.0");
		prop.put("encoding", "UTF-8");
		indentOneTime("<?xml", prop, "?>", fileWriter, true);
		prop.clear();

		prop.put("xmlns", "http://www.springframework.org/schema/mvc");
		prop.put("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
		prop.put("xmlns:beans", "http://www.springframework.org/schema/beans");
		prop.put("xmlns:context",
				"http://www.springframework.org/schema/context");
		prop.put(
				"xsi:schemaLocation",
				"http://www.springframework.org/schema/mvc "
						+ "http://www.springframework.org/schema/mvc/spring-mvc.xsd "
						+ "http://www.springframework.org/schema/beans "
						+ "http://www.springframework.org/schema/beans/spring-beans.xsd "
						+ "http://www.springframework.org/schema/context "
						+ "http://www.springframework.org/schema/context/spring-context.xsd");
		indentOneTime("<beans:beans", prop, ">", fileWriter, false);

		appendToFile(fileWriter,
				"<!-- DispatcherServlet Context: defines this servlet's "
						+ "request-processing infrastructure -->\n");

		appendToFile(fileWriter, "\n");
		if (config.isAnnotation()) {
			appendToFile(fileWriter, "<!-- Enables the Spring MVC @Controller "
					+ "programming model -->\n");
			appendToFile(fileWriter, "<annotation-driven />\n");
			appendToFile(fileWriter, "\n");
		}

		if (config.getComponentScanPackage() != null
				&& !"".equals(config.getComponentScanPackage())) {
			prop.clear();
			prop.put("base-package", config.getComponentScanPackage());
			indentOneTime("<context:component-scan", prop, " />", fileWriter,
					true);

			appendToFile(fileWriter, "\n");
		}

		// Create all of bean here

		List<ClassInformation> classInformations = config
				.getClassInformations();
		for (ClassInformation classInformation : classInformations) {
			prop.clear();
			if (classInformation.getId() != null
					&& !"".equals(classInformation.getId()))
				prop.put("id", classInformation.getId());
			prop.put("class", classInformation.getClassName());

			indentOneTime("<beans:bean", prop, ">", fileWriter, false);

			addPropertyToTag(fileWriter, classInformation.getChildTags());

			decreaseIndentOneTime("</beans:bean>", fileWriter);
		}

		List<Tag> tags = config.getTags();
		for (Tag tag : tags)
			addTag(fileWriter, tag);

		// Property place holder
		if (config.getPlaceHolder() != null
				&& !"".equals(config.getPlaceHolder())) {
			String sPlaceHolder = config.getPlaceHolder();
			if (sPlaceHolder.indexOf("classpath") != -1)
				sPlaceHolder = sPlaceHolder
						.substring(sPlaceHolder.indexOf(':') + 1);
			File placeHolder = new File(folderPath.getSrcPath() + '/'
					+ sPlaceHolder);

			if (placeHolder.exists())
				appendToFile(fileWriter,
						"<context:property-placeholder location=\"classpath:"
								+ sPlaceHolder + "\" />\n");
		}
		decreaseIndentOneTime("</beans:beans>", fileWriter);
	}

	private void addPropertyToTag(FileWriter fileWriter, List<Tag> tags)
			throws IOException {
		if (tags != null && tags.size() != 0) {
			for (Tag tag : tags) {
				addTag(fileWriter, tag);
			}
		}
	}

	private void addTag(FileWriter fileWriter, Tag tag) throws IOException {
		StringBuffer sbuff = new StringBuffer();

		sbuff.append('<' + tag.getType());
		Properties prop = tag.getProp();
		if (prop != null) {
			Enumeration<Object> keys = prop.keys();

			while (keys.hasMoreElements()) {
				sbuff.append(" ");
				String key = (String) keys.nextElement();
				sbuff.append(key + " = \"");

				Object value = prop.getProperty(key);

				if (value.getClass().getSimpleName().equalsIgnoreCase("String"))
					sbuff.append(value + "\"");
			}
		}
		List<Tag> tags = tag.getchildTags();
		if (tags != null && tags.size() > 0) {
			sbuff.append(">");
			indentOneTime(sbuff.toString(), null, "", fileWriter, false);
			for (Tag childTag : tags)
				addTag(fileWriter, childTag);

			decreaseIndentOneTime("</" + tag.getType() + '>', fileWriter);
		} else if (tag.getContent() != null
				&& checkSenseWithContent(tag.getContent())) {
			sbuff.append(">");
			String contentOfTag = tag.getContent();
			Pattern p = Pattern
					.compile("^[\\n\\t]*([\\.a-zA-Z0-9_-]+)[\\n\\t]*$");
			Matcher m = p.matcher(contentOfTag);

			if (m.matches()) {
				contentOfTag = m.group(1);
			}
			
			indentOneTime(sbuff.toString(), null, "", fileWriter, false);
			appendToFile(fileWriter, contentOfTag + '\n');

			decreaseIndentOneTime("</" + tag.getType() + '>', fileWriter);
		} else {
			sbuff.append(" />");
			indentOneTime(sbuff.toString(), null, "", fileWriter, true);
		}

	}

	private boolean checkSenseWithContent(String content) {
		return Pattern
				.matches("^[\\n\\t]*[\\.a-zA-Z0-9_-]+[\\n\\t]*$", content);
	}

	private void generateWebConfig(FileWriter fileWriter) throws IOException {
		Properties prop = new Properties();
		prop.put("version", "1.0");
		prop.put("encoding", "UTF-8");
		indentOneTime("<?xml", prop, "?>", fileWriter, true);
		prop.clear();

		prop.put("version", "2.5");
		prop.put("xmlns", "http://java.sun.com/xml/ns/javaee");
		prop.put("xmlns:xsi", "http://www.w3.org/2001/XMLScheme-instance");
		prop.put(
				"xsi:schemaLocation",
				"http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd");
		indentOneTime("<web-app", prop, ">", fileWriter, false);

		indentOneTime("<context-param>", null, "", fileWriter, false);
		appendToFile(fileWriter,
				"<param-name>contextConfigLocation</param-name>\n");
		appendToFile(fileWriter,
				"<param-value>/WEB-INF/spring/root-context.xml</param-value>\n");
		decreaseIndentOneTime("</context-param>", fileWriter);

		indentOneTime("<listener>", null, "", fileWriter, false);
		appendToFile(fileWriter,
				"<listener-class>org.springframework.web.context."
						+ "ContextLoaderListener</listener-class>\n");
		decreaseIndentOneTime("</listener>", fileWriter);

		indentOneTime("<servlet>", null, "", fileWriter, false);
		appendToFile(fileWriter, "<servlet-name>appServlet</servlet-name>\n");
		appendToFile(fileWriter,
				"<servlet-class>org.springframework.web.servlet."
						+ "DispatcherServlet</servlet-class>\n");
		indentOneTime("<init-param>", null, "", fileWriter, false);
		appendToFile(fileWriter,
				"<param-name>contextConfigLocation</param-name>\n");
		appendToFile(fileWriter,
				"<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>\n");
		decreaseIndentOneTime("</init-param>", fileWriter);
		appendToFile(fileWriter, "<load-on-startup>1</load-on-startup>\n");
		decreaseIndentOneTime("</servlet>", fileWriter);

		indentOneTime("<servlet-mapping>", null, "", fileWriter, false);
		appendToFile(fileWriter, "<servlet-name>appServlet</servlet-name>\n");
		appendToFile(fileWriter, "<url-pattern>/</url-pattern>\n");
		decreaseIndentOneTime("</servlet-mapping>", fileWriter);

		decreaseIndentOneTime("</web-app>", fileWriter);
	}
}